package com.eage.security.config;

import com.eage.security.properties.CompanyProperties;
import com.eage.security.login.impl.FormUserLoginImpl;
import com.eage.security.login.impl.MobileUserLoginImpl;
import com.eage.security.login.impl.SocialUserLoginImpl;
import com.eage.security.login.inter.UserLogin;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
@Slf4j
public class DefaultShiroConfig {

    @Autowired
    private CompanyProperties companyProperties;

    @Autowired
    private FormUserLoginImpl formUserProviderImpl;

    @Autowired
    private SocialUserLoginImpl socialUserProviderImpl;

    @Autowired
    private MobileUserLoginImpl mobileUserProviderImpl;

    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    //@Bean()
    //public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
    //    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    //    shiroFilterFactoryBean.setSecurityManager(securityManager);
    //    Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
    //    filters.put("authc", new NoLoginFilter());//将自定义 的未登入跳转注入shiroFilter中 =>前后端分离 未授权并不是跳页面,而是自定义返回json 即setLoginUrl具体方法重写
    //    filters.put("roles", new NoRolesFilter());//将自定义 的权限跳转注入shiroFilter中 =>前后端分离 未授权并不是跳页面,而是自定义返回json  即setUnauthorizedUrl具体方法重写
    //    filters.put("perms", new NoPermsFilter());//将自定义 的权限跳转注入shiroFilter中 =>前后端分离 未授权并不是跳页面,而是自定义返回json  即setUnauthorizedUrl具体方法重写
    //    /**
    //     *  shiro内置过滤器
    //     *  常用过滤器 :
    //     *  anon:无需登入 认证考研访问
    //     *  authc: 必须认证才可以访问
    //     *  user:如果使用了rememberMe可以直接访问
    //     *  perms 该资源必须要有资源权限才可以访问
    //     *  role 必须得到角色权限才可以访问
    //     */
    //    Map<String, String> filterMap = new LinkedHashMap<>();
    //    //filterMap.put("/**", "anon");
    //    //不能拦截登录接口
    //    filterMap.put("/shiro/mp/login", "anon");
    //    filterMap.put("/**", "authc");
    //    log.info("当前系统拦截模式为:{}", filterMap);
    //    //设置未授权提示页面
    //    shiroFilterFactoryBean.setUnauthorizedUrl("/admin/page/noAuth");
    //    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
    //    //设置未认证 未登入用户的跳转页面,
    //    // 前后端不分离jsp情况下,写页面控制器跳转
    //    // 前后端分离情况下,跳转接口,接口再发送json数据,将页面跳转权利交给前端,只需要协调状态码就行!
    //    shiroFilterFactoryBean.setLoginUrl("/shiro/mp/nologinCode");//跳转接口 不会被拦截!
    //    return shiroFilterFactoryBean;
    //}

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

    /**
     * ### deleteInvalidSessions (需要sessionValidationSchedulerEnabled 开启) 失效后是否删除相应的session(redis)中的key 配合globalSessionTimeout  ###
     * <p>
     * ### sessionManager.globalSessionTimeout：(需要sessionValidationSchedulerEnabled 开启) 设置全局会话超时时间，默认30分钟，即如果30分钟内没有访问会话将过期；
     * ***解释: 首先shiro内部算法类似Map. sessionId对应session(redis)的key  单位时间内删除shiro内部算法的sessionId和session(redis)绑定关系.也就是说redis即使设置很久,但是这个globalSessionTimeout只设置10秒. 10秒之后shiro内部和session(redis的key)绑定关系也会被删除 sessionid和session关系解除.所以必须这个时间等于session过期时间,放在redis中就是,这个时间等于redis过期时间*** ###
     * <p>
     * ### sessionManager.sessionValidationSchedulerEnabled：是否开启会话验证器，默认是开启的；
     * <p>
     * sessionValidationScheduler：会话验证调度器，sessionManager默认就是使用ExecutorServiceSessionValidationScheduler，其使用JDK的ScheduledExecutorService进行定期调度并验证会话是否过期；
     * <p>
     * sessionValidationScheduler.interval：设置调度时间间隔，单位毫秒，默认就是1小时；
     * <p>
     * sessionValidationScheduler.sessionManager：设置会话验证调度器进行会话验证时的会话管理器；
     * <p>
     * <p>
     * <p>
     * sessionManager.sessionValidationScheduler：设置会话验证调度器，默认就是使用ExecutorServiceSessionValidationScheduler。
     * <p>
     * ### => 这里的做法是globalSessionTimeout过期时间和session(redis)中的过期时间相同 ,session过期的时候redis也同步失效###
     *
     * @return
     */
    @Bean
    public SessionManager sessionManager(SessionDAO sessionDAO) {
        //配置sessionId获取方式.这里用ajax请求头
        MySessionManager mySessionManager = new MySessionManager(companyProperties);
        //设置sessionId对应session(redis)的key 的绑定关系时间 可以理解为session过期时间.和redis中过期时间最好相同 .否则即使redis设置无限大,也会出异常
        //解释: 这个时间表示 sessionId对应session(redis)的key 的绑定关系时间(如果deleteInvalidSessions开启.则会发现 sessionId存在,但是(session)redis 的key一起消失),如果发现redis中的key被删除是因为deleteInvalidSessions 开启了
        mySessionManager.setGlobalSessionTimeout(companyProperties.getShiroRedisSession().getTimeout() * 1000);
        //配置session获得方式  最终是SessionDAO类获取session会话.所以要重写
        mySessionManager.setSessionDAO(sessionDAO);
        return mySessionManager;
    }

    @Bean
    public SessionDAO sessionDAO(RedisManager redisManager) {
        log.error("使用redis缓存");
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager);
        //shiroRedisSession前缀,保存在redisSession里面的key前缀[用户的sessionId].项目名+模块名,可以让redisManager可视化工具做到每个端分包查看,做到分拣效果而已
        String shiroPrefix = companyProperties.getShiroRedisSession().getName() + ":" + companyProperties.getShiroRedisSession().getUniqueWebProject() + ":";
        redisSessionDAO.setKeyPrefix(shiroPrefix);
        log.warn("shiroPrefix:[ {} ]", shiroPrefix);
        log.info("设置redisSessionDAO");
        return redisSessionDAO;
    }

    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(companyProperties.getShiroRedisSession().getHost() + ":" + companyProperties.getShiroRedisSession().getPort());
        redisManager.setPassword(companyProperties.getShiroRedisSession().getPassword());
        redisManager.setTimeout(companyProperties.getShiroRedisSession().getTimeout());// 配置过期时间
        redisManager.setDatabase(companyProperties.getShiroRedisSession().getDatabase());
        log.warn("配置redis连接设置###" + companyProperties.getShiroRedisSession().getHost() + "###"
                + companyProperties.getShiroRedisSession().getPort() + "###"
                + companyProperties.getShiroRedisSession().getDatabase());
        return redisManager;
    }

    @Bean(name = "userRealm")
    public UserRealm getRealm() {
        Map<String, UserLogin> userProviderMap = new HashMap();
        userProviderMap.put(StringUtils.uncapitalize(formUserProviderImpl.getClass().getSimpleName()), formUserProviderImpl);
        userProviderMap.put(StringUtils.uncapitalize(socialUserProviderImpl.getClass().getSimpleName()), socialUserProviderImpl);
        userProviderMap.put(StringUtils.uncapitalize(mobileUserProviderImpl.getClass().getSimpleName()), mobileUserProviderImpl);
        return new UserRealm(userProviderMap);
    }

}